"""
SSL 证书管理 API
所有接口采用 POST 方法，统一响应格式
"""
from fastapi import APIRouter, Depends, Request
from motor.motor_asyncio import AsyncIOMotorDatabase
from pydantic import BaseModel, Field
from typing import List, Optional
from bson import ObjectId
from datetime import datetime, timedelta
import zipfile
import io
import logging

logger = logging.getLogger(__name__)

from database.connection import get_database
from models.certificate import (
    CertificateCreate,
    CertificateOut,
    CertificateDetail,
    ListCertificatesQuery,
    CertificateDownloadRequest,
    RenewCertificateRequest,
    SubmitDNSValidationRequest,
    CertificateStatus,
    DNSValidationType
)
from api.deps_v1 import get_current_user
from utils.response import success, error
from models.response import ErrorCode
from utils.crypto import crypto_service
from services.audit import log_audit
from models.audit import AuditAction, AuditStatus, ResourceType

router = APIRouter(prefix="/sac/v1/certificates", tags=["证书服务"])

# 服务名称
SERVICE_NAME = "certificates"


# ========== 请求模型 ==========

class ApplyCertificateRequest(CertificateCreate):
    """申请证书请求"""
    pass


class ListCertificatesRequest(ListCertificatesQuery):
    """获取证书列表请求"""
    pass


class DescribeCertificateRequest(BaseModel):
    """获取证书详情请求"""
    certificate_id: str = Field(..., description="证书 ID")
    include_private_key: bool = Field(default=False, description="是否返回私钥")


class DeleteCertificateRequest(BaseModel):
    """删除证书请求"""
    certificate_id: str = Field(..., description="证书 ID")


# ========== 响应模型 ==========

class ApplyCertificateResult(BaseModel):
    """申请证书结果"""
    certificate_id: str
    status: str
    message: str
    dns_validation_records: Optional[List[dict]] = None


class ListCertificatesResult(BaseModel):
    """证书列表结果"""
    certificates: List[CertificateOut]
    total: int
    skip: int
    limit: int


class DescribeCertificateResult(BaseModel):
    """证书详情结果"""
    certificate: CertificateDetail


class DownloadCertificateResult(BaseModel):
    """证书下载结果"""
    filename: str
    content_type: str
    content: str  # base64 编码的内容


class RenewCertificateResult(BaseModel):
    """续签证书结果"""
    certificate_id: str
    status: str
    message: str


class SubmitDNSValidationResult(BaseModel):
    """DNS 验证结果"""
    certificate_id: str
    status: str
    message: str
    validated_domains: List[str]


class DeleteCertificateResult(BaseModel):
    """删除证书结果"""
    certificate_id: str
    message: str


# ========== API 接口 ==========

@router.post("/ApplyCertificate", summary="申请证书")
async def apply_certificate(
    request: Request,
    body: ApplyCertificateRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    申请 SSL 证书
    
    支持 Let's Encrypt 和 ZeroSSL
    支持 DNS-01 自动和手动验证
    
    **自动验证模式**:
    - 需要提供 dns_credential_id
    - 系统自动添加 DNS 记录并验证
    
    **手动验证模式**:
    - 返回需要添加的 DNS 记录
    - 用户手动添加后调用 SubmitDNSValidation
    """
    try:
        request_id = request.state.request_id
        
        # 检查域名是否已存在待处理的证书
        existing = await db.certificates.find_one({
            "user_id": current_user["id"],
            "domain": body.domain,
            "status": {"$in": [CertificateStatus.PENDING.value, CertificateStatus.ISSUED.value]}
        })
        
        if existing:
            await log_audit(
                db, current_user["id"], AuditAction.CREATE,
                ResourceType.CERTIFICATE, AuditStatus.FAILED,
                request_id, details={"domain": body.domain, "error": "证书已存在"}
            )
            return error(
                request, "ApplyCertificate", SERVICE_NAME,
                ErrorCode.CERT_ALREADY_EXISTS,
                detail=f"域名 {body.domain} 已有证书存在"
            )
        
        # 如果是自动模式，检查 DNS 凭据
        if body.validation_type == DNSValidationType.DNS_01_AUTO:
            if not body.dns_credential_id:
                return error(
                    request, "ApplyCertificate", SERVICE_NAME,
                    ErrorCode.CERT_DNS_CREDENTIAL_NOT_FOUND,
                    detail="自动验证模式需要提供 dns_credential_id"
                )
        
        # 创建证书记录
        cert_data = {
            "user_id": current_user["id"],
            "domain": body.domain,
            "sans": body.sans,
            "ca_provider": body.ca_provider.value,
            "dns_credential_id": body.dns_credential_id,
            "auto_renew": body.auto_renew,
            "status": CertificateStatus.PENDING.value,
            "cert_fullchain_pem": None,
            "key_encrypted": None,
            "expires_at": None,
            "issued_at": None,
            "ttl_deletion_at": None,
            "deleted_at": None,
            "created_at": datetime.utcnow(),
            "updated_at": None,
            "dns_validation_records": []
        }
        
        result = await db.certificates.insert_one(cert_data)
        cert_id = str(result.inserted_id)
        
        # 记录审计日志
        await log_audit(
            db, current_user["id"], AuditAction.APPLY_CERTIFICATE,
            ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
            request_id, resource_id=cert_id,
            details={
                "domain": body.domain,
                "ca_provider": body.ca_provider.value,
                "validation_type": body.validation_type.value
            }
        )
        
        # 获取 DNS 验证记录
        dns_records = None
        if body.validation_type == DNSValidationType.DNS_01_MANUAL:
            try:
                from services.certificate_service import CertificateService
                
                # 获取真实的 DNS 验证信息
                domains = [body.domain] + body.sans
                dns_records = await CertificateService.get_dns_validation_info(
                    domains=domains,
                    ca_provider=body.ca_provider.value,
                    contact_email=body.contact_email
                )
                
                # 保存 DNS 验证记录到数据库
                await db.certificates.update_one(
                    {"_id": ObjectId(cert_id)},
                    {"$set": {"dns_validation_records": dns_records}}
                )
            except Exception as e:
                logger.warning(f"获取 DNS 验证信息失败: {str(e)}，使用占位符")
                # 如果失败，使用占位符
                dns_records = [
                    {
                        "domain": body.domain,
                        "record_name": f"_acme-challenge.{body.domain}",
                        "record_type": "TXT",
                        "record_value": "请稍后刷新获取验证值",
                        "validated": False
                    }
                ]
                for san_domain in body.sans:
                    dns_records.append({
                        "domain": san_domain,
                        "record_name": f"_acme-challenge.{san_domain}",
                        "record_type": "TXT",
                        "record_value": "请稍后刷新获取验证值",
                        "validated": False
                    })
        
        return success(
            request, "ApplyCertificate", SERVICE_NAME,
            ApplyCertificateResult(
                certificate_id=cert_id,
                status="pending",
                message="证书申请已创建，请使用第三方 ACME 客户端申请证书后上传",
                dns_validation_records=dns_records
            ).model_dump()
        )
        
    except ValueError as e:
        return error(
            request, "ApplyCertificate", SERVICE_NAME,
            ErrorCode.CERT_DOMAIN_INVALID,
            detail=str(e)
        )
    except Exception as e:
        return error(
            request, "ApplyCertificate", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/ListCertificates", summary="获取证书列表")
async def list_certificates(
    request: Request,
    body: ListCertificatesRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    获取当前用户的证书列表（分页）
    
    支持按域名、状态、CA 提供商过滤
    """
    try:
        # 构建查询条件
        query = {
            "user_id": current_user["id"],
            "deleted_at": None  # 排除已删除的
        }
        
        if body.domain:
            query["domain"] = {"$regex": body.domain, "$options": "i"}
        
        if body.status:
            query["status"] = body.status.value
        
        if body.ca_provider:
            query["ca_provider"] = body.ca_provider.value
        
        if body.auto_renew is not None:
            query["auto_renew"] = body.auto_renew
        
        # 查询总数
        total = await db.certificates.count_documents(query)
        
        # 查询列表
        cursor = db.certificates.find(query)\
            .sort("created_at", -1)\
            .skip(body.skip)\
            .limit(body.limit)
        
        certs = await cursor.to_list(length=body.limit)
        
        # 转换数据格式
        cert_list = []
        for cert in certs:
            cert["id"] = str(cert.pop("_id"))
            cert["has_private_key"] = bool(cert.get("key_encrypted"))
            # 不返回加密的私钥
            cert.pop("key_encrypted", None)
            cert_list.append(CertificateOut(**cert))
        
        return success(
            request, "ListCertificates", SERVICE_NAME,
            ListCertificatesResult(
                certificates=cert_list,
                total=total,
                skip=body.skip,
                limit=body.limit
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "ListCertificates", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/DescribeCertificate", summary="获取证书详情")
async def describe_certificate(
    request: Request,
    body: DescribeCertificateRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    获取指定证书的详细信息
    
    可选择是否返回私钥（需要特殊权限）
    """
    try:
        request_id = request.state.request_id
        
        if not ObjectId.is_valid(body.certificate_id):
            return error(
                request, "DescribeCertificate", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="无效的证书 ID"
            )
        
        # 查询证书
        cert = await db.certificates.find_one({
            "_id": ObjectId(body.certificate_id),
            "user_id": current_user["id"],
            "deleted_at": None
        })
        
        if not cert:
            return error(
                request, "DescribeCertificate", SERVICE_NAME,
                ErrorCode.CERT_NOT_FOUND
            )
        
        cert["id"] = str(cert.pop("_id"))
        
        # 解密私钥（如果需要）
        key_pem = None
        if body.include_private_key and cert.get("key_encrypted"):
            try:
                key_pem = crypto_service.decrypt_private_key(cert["key_encrypted"])
                
                # 记录审计日志（访问私钥）
                await log_audit(
                    db, current_user["id"], AuditAction.DOWNLOAD_CERTIFICATE,
                    ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
                    request_id, resource_id=body.certificate_id,
                    details={"action": "access_private_key"}
                )
            except Exception as e:
                key_pem = None
        
        # 移除加密的私钥
        cert.pop("key_encrypted", None)
        
        # 构建详情对象
        cert_detail = CertificateDetail(**cert)
        cert_detail.key_pem = key_pem
        cert_detail.has_private_key = bool(key_pem or cert.get("key_encrypted"))
        
        return success(
            request, "DescribeCertificate", SERVICE_NAME,
            DescribeCertificateResult(certificate=cert_detail).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "DescribeCertificate", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/DownloadCertificate", summary="下载证书")
async def download_certificate(
    request: Request,
    body: CertificateDownloadRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    下载证书
    
    支持格式：
    - pem: 纯文本 PEM 格式
    - zip: 压缩包（包含证书和私钥）
    - pfx: PKCS#12 格式（TODO）
    """
    try:
        request_id = request.state.request_id
        
        if not ObjectId.is_valid(body.certificate_id):
            return error(
                request, "DownloadCertificate", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="无效的证书 ID"
            )
        
        # 查询证书
        cert = await db.certificates.find_one({
            "_id": ObjectId(body.certificate_id),
            "user_id": current_user["id"],
            "deleted_at": None
        })
        
        if not cert:
            return error(
                request, "DownloadCertificate", SERVICE_NAME,
                ErrorCode.CERT_NOT_FOUND
            )
        
        if cert["status"] != CertificateStatus.ISSUED.value:
            return error(
                request, "DownloadCertificate", SERVICE_NAME,
                ErrorCode.CERT_NOT_READY,
                detail="证书尚未签发完成"
            )
        
        # 解密私钥
        key_pem = ""
        if body.include_private_key and cert.get("key_encrypted"):
            key_pem = crypto_service.decrypt_private_key(cert["key_encrypted"])
        
        cert_pem = cert.get("cert_fullchain_pem", "")
        domain = cert["domain"]
        
        # 根据格式生成下载内容
        if body.format.value == "pem":
            # PEM 格式
            content = f"{cert_pem}\n{key_pem}" if key_pem else cert_pem
            filename = f"{domain}.pem"
            content_type = "application/x-pem-file"
            
        elif body.format.value == "zip":
            # ZIP 格式
            buffer = io.BytesIO()
            with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
                zip_file.writestr(f"{domain}.crt", cert_pem)
                if key_pem:
                    zip_file.writestr(f"{domain}.key", key_pem)
            
            import base64
            content = base64.b64encode(buffer.getvalue()).decode()
            filename = f"{domain}.zip"
            content_type = "application/zip"
            
        else:
            return error(
                request, "DownloadCertificate", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="暂不支持该格式"
            )
        
        # 记录审计日志
        await log_audit(
            db, current_user["id"], AuditAction.DOWNLOAD_CERTIFICATE,
            ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
            request_id, resource_id=body.certificate_id,
            details={"format": body.format.value, "domain": domain}
        )
        
        return success(
            request, "DownloadCertificate", SERVICE_NAME,
            DownloadCertificateResult(
                filename=filename,
                content_type=content_type,
                content=content
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "DownloadCertificate", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/RenewCertificate", summary="续签证书")
async def renew_certificate(
    request: Request,
    body: RenewCertificateRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    续签证书
    
    支持强制续签（即使未到期）
    """
    try:
        request_id = request.state.request_id
        
        if not ObjectId.is_valid(body.certificate_id):
            return error(
                request, "RenewCertificate", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="无效的证书 ID"
            )
        
        # 查询证书
        cert = await db.certificates.find_one({
            "_id": ObjectId(body.certificate_id),
            "user_id": current_user["id"],
            "deleted_at": None
        })
        
        if not cert:
            return error(
                request, "RenewCertificate", SERVICE_NAME,
                ErrorCode.CERT_NOT_FOUND
            )
        
        # 检查是否需要续签
        if not body.force and cert.get("expires_at"):
            days_left = (cert["expires_at"] - datetime.utcnow()).days
            if days_left > 30:
                return error(
                    request, "RenewCertificate", SERVICE_NAME,
                    ErrorCode.INVALID_OPERATION,
                    detail=f"证书还有 {days_left} 天到期，无需续签。使用 force=true 强制续签。"
                )
        
        # 更新证书状态为待续签
        await db.certificates.update_one(
            {"_id": ObjectId(body.certificate_id)},
            {"$set": {
                "status": CertificateStatus.PENDING.value,
                "updated_at": datetime.utcnow()
            }}
        )
        
        # 记录审计日志
        await log_audit(
            db, current_user["id"], AuditAction.RENEW_CERTIFICATE,
            ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
            request_id, resource_id=body.certificate_id,
            details={"force": body.force}
        )
        
        return success(
            request, "RenewCertificate", SERVICE_NAME,
            RenewCertificateResult(
                certificate_id=body.certificate_id,
                status="pending",
                message="证书已标记为待续签，请手动续签后重新上传证书"
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "RenewCertificate", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/DeleteCertificate", summary="删除证书")
async def delete_certificate(
    request: Request,
    body: DeleteCertificateRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    删除证书（软删除）
    
    证书不会立即从数据库删除，而是标记为已删除
    """
    try:
        request_id = request.state.request_id
        
        if not ObjectId.is_valid(body.certificate_id):
            return error(
                request, "DeleteCertificate", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="无效的证书 ID"
            )
        
        # 更新证书状态（软删除）
        result = await db.certificates.update_one(
            {
                "_id": ObjectId(body.certificate_id),
                "user_id": current_user["id"],
                "deleted_at": None
            },
            {
                "$set": {
                    "deleted_at": datetime.utcnow(),
                    "updated_at": datetime.utcnow()
                }
            }
        )
        
        if result.matched_count == 0:
            return error(
                request, "DeleteCertificate", SERVICE_NAME,
                ErrorCode.CERT_NOT_FOUND
            )
        
        # 记录审计日志
        await log_audit(
            db, current_user["id"], AuditAction.DELETE,
            ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
            request_id, resource_id=body.certificate_id
        )
        
        return success(
            request, "DeleteCertificate", SERVICE_NAME,
            DeleteCertificateResult(
                certificate_id=body.certificate_id,
                message="证书已删除"
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "DeleteCertificate", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/SubmitDNSValidation", summary="提交 DNS 验证")
async def submit_dns_validation(
    request: Request,
    body: SubmitDNSValidationRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    提交 DNS 验证（手动模式）
    
    用户手动添加 DNS 记录后，调用此接口触发验证
    """
    try:
        request_id = request.state.request_id
        
        if not ObjectId.is_valid(body.certificate_id):
            return error(
                request, "SubmitDNSValidation", SERVICE_NAME,
                ErrorCode.INVALID_PARAMETER,
                detail="无效的证书 ID"
            )
        
        # 查询证书
        cert = await db.certificates.find_one({
            "_id": ObjectId(body.certificate_id),
            "user_id": current_user["id"],
            "deleted_at": None
        })
        
        if not cert:
            return error(
                request, "SubmitDNSValidation", SERVICE_NAME,
                ErrorCode.CERT_NOT_FOUND
            )
        
        if cert["status"] != CertificateStatus.PENDING.value:
            return error(
                request, "SubmitDNSValidation", SERVICE_NAME,
                ErrorCode.INVALID_OPERATION,
                detail="证书状态不正确"
            )
        
        # 执行 DNS 验证并签发证书
        try:
            from services.certificate_service import CertificateService
            
            domains = [cert["domain"]] + cert.get("sans", [])
            
            result = await CertificateService.verify_and_issue(
                db=db,
                cert_id=body.certificate_id,
                domains=domains,
                ca_provider=cert["ca_provider"],
                contact_email=current_user.get("email", ""),
                algorithm="RSA"
            )
            
            # 记录审计日志
            await log_audit(
                db, current_user["id"], AuditAction.CREATE,
                ResourceType.CERTIFICATE, AuditStatus.SUCCESS,
                request_id, resource_id=body.certificate_id,
                details={"action": "verify_dns_and_issue", "result": result}
            )
            
            return success(
                request, "SubmitDNSValidation", SERVICE_NAME,
                SubmitDNSValidationResult(
                    certificate_id=body.certificate_id,
                    status="issued",
                    message="证书签发成功",
                    validated_domains=domains
                ).model_dump()
            )
        except Exception as e:
            logger.error(f"DNS 验证失败: {str(e)}")
            return error(
                request, "SubmitDNSValidation", SERVICE_NAME,
                ErrorCode.DNS_VALIDATION_FAILED,
                detail=str(e)
            )
        
    except Exception as e:
        return error(
            request, "SubmitDNSValidation", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )

